Admin Users: persist requested storage, rename and regroup filters, add clear action - #6096
Draft
akolson wants to merge 8 commits into
Draft
Admin Users: persist requested storage, rename and regroup filters, add clear action#6096akolson wants to merge 8 commits into
akolson wants to merge 8 commits into
Conversation
A storage request only ever sent an email, so the admin Users CSV "Storage needed" column could only show what a user typed at registration. Users who requested storage afterwards showed blank. Persist the requested amount to user.information and prefer it over the registration answer when building the CSV row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Has Studio edits" matched any Change row the user ever created, so it reported activity rather than edits; renamed to "Has Studio activity" in the filter row and the CSV header. On wide viewports the two checkboxes sat in separate quarter-width columns, leaving a large gap between them. They now share one half-width flex row alongside a "Clear filters" action, which resets every filter while preserving pagination and sorting. The action is enabled only once a filter differs from the default its control already displays. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The spec drove the component through wrapper.vm — assigning filters and reading computed properties — so it asserted internals rather than behaviour. Rewritten against Vue Testing Library, per the frontend testing guidelines, querying by label and role and asserting on the dispatched fetch payload. Two problems surfaced once the queries went through the accessibility tree. "Clear filters" used appearance="basic-link", which KButton renders as an anchor; disabled is not a valid attribute there and KButton's click handler does not guard on it, so the greyed-out link was still focusable and still fired. It is now shown only when there is something to clear. Studio's IconButton passes ariaLabel="text" unbound, leaving icon buttons with no usable accessible name, so those two are still reached by their existing data-test hooks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
akolson
force-pushed
the
add-requested-storage-value
branch
from
August 19, 2026 10:31
3715d35 to
456055a
Compare
Every user-facing string on the page was hardcoded English, including the browser tab title, the filter option labels, the table headers and the CSV snackbars. Moved them into a usersStrings translator module, each with a context note for translators, and replaced the count strings' ternaries with ICU plurals. The spec references the message keys rather than the English text, per the frontend testing guidelines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the Vuetify layout and form controls above the users table with their Kolibri Design System equivalents: VLayout/VFlex become KGrid and KGridItem, VSelect becomes KSelect, VTextField becomes KTextbox with a prepended KIcon, and VProgressLinear becomes KLinearLoader. KSelect emits `select` only on user action, so the filters bind `:value` and `@select` rather than v-model, which would otherwise write the defaults back into the URL and undo "Clear filters". VDataTable, CountryField and UserItem still wrap Vuetify and are left for a later pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The component mixed an Options API shell with composables. Move it wholly into `<script setup>`: the Vuex getters become computeds over useStore, routerMixin's tab title becomes an onMounted call to updateTabTitle, and the responsive headers become a plain function of the breakpoint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four comments restated what the code already said: two doc blocks naming their own helpers, and a pointer to renderWithFilters repeated above two calls to it. The remaining comments each cite behaviour of code outside this file that a test has to work around. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CountryField gains fullWidth, which switches its own max-width from 500px to 100%. KTextbox caps the inner UiTextbox at max-width: 400px, which a pass-through class cannot reach, so it is overridden through the component's appearanceOverrides prop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
akolson
force-pushed
the
add-requested-storage-value
branch
from
August 20, 2026 15:25
b07ec95 to
7cde310
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This fixes QA follow-ups from #5946, all on the admin Users page.
information["space_needed"]is only written at registration, and only when the user ticks "Storing materials for private or local use" — the post-registration storage request flow just sent an email and persisted nothing. So users who requested storage after signing up showed blank, while older users who answered the registration question showed a value.StorageSettingsViewnow records the requested amount on the user, and the CSV prefers it over the registration answer. No migration:informationis an existingJSONField.Exists(Change.objects.filter(created_by=...)), which matches anyChangerow the user ever created — creating a channel qualifies, and deleting it later doesn't undo it. That is activity, not edits, which is what QA observed. Renamed in the filter row and the CSV header; the query param and backend annotation are unchanged, so existing bookmarked URLs still work.On sign-up (storage specified)

On request storage (storage requested)

UI updates

Before
After

References
Fixes #5946
Reviewer guidance
AI usage
Claude Code (Opus 5) diagnosed the reported bugs and proposed fixes and a refactoring plan. The code and approach were reviewed and verified by @akolson.